/*
* FaceCat图形通讯框架(非开源)
* 著作权编号:2015SR229355+2020SR0266727
* 上海卷卷猫信息技术有限公司
*/

package facecat.topin.sock;

import java.io.*;
import java.net.*;
import java.util.*;

/*
* 客户端套接字连接
*/
public class FCClientSocket implements Runnable {
    public FCClientSocket(long proxyType, String ip, int port, String proxyIp, int proxyPort, String proxyUserName, String proxyUserPwd, String proxyDomain, int timeout) {
        m_blnProxyServerOk = true;
        m_proxyDomain = proxyDomain;
        m_proxyType = proxyType;
        m_ip = ip;
        m_port = port;
        m_proxyIp = proxyIp;
        m_proxyPort = proxyPort;
        m_proxyUserName = proxyUserName;
        m_proxyUserPwd = proxyUserPwd;
        m_timeout = timeout;
    }

    public void finalize() throws Throwable {
        delete();
    }

    private boolean m_blnProxyServerOk = false;
    private boolean m_connected = false;
    public int m_hSocket;
    private DataInputStream m_inputStream = null;
    private String m_ip;
    private boolean m_isDeleted;
    private int m_port;
    private String m_proxyDomain;
    private long m_proxyType;
    private String m_proxyIp;
    private int m_proxyPort;
    private String m_proxyUserName;
    private String m_proxyUserPwd;
    private ArrayList<byte[]> m_sendDatas = new ArrayList<byte[]>();
    private Socket m_socket = null;
    int m_timeout;

    public int close() {
        int ret = -1;
        if (m_socket != null) {
            try {
                m_socket.close();
                ret = 1;
            } catch (Exception ex) {
                byte[] rmsg = new byte[1];
                rmsg[0] = (byte) ((char) 2);
                FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg, 1);
                FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 2, "socket exit");
                ret = -1;
            }
        }
        m_connected = false;
        return ret;
    }

    public ConnectStatus connect() {
        return connectStandard();
    }
    
    public static boolean noThread = false;

    private ConnectStatus connectStandard() {
        ConnectStatus status = ConnectStatus.CONNECT_SERVER_FAIL;

        if (m_socket == null) {
            try {
                m_socket = new Socket();
                InetSocketAddress socketAddress = new InetSocketAddress(m_ip, m_port);
                m_socket.connect(socketAddress, m_timeout * 1000);
                m_inputStream = new DataInputStream(m_socket.getInputStream());
                m_connected = true;
                status = ConnectStatus.SUCCESS;
                byte[] submitStr = new byte[4];
                submitStr[0] = (byte)'m';
                submitStr[1] = (byte)'i';
                submitStr[2] = (byte)'a';
                submitStr[3] = (byte)'o';
                send(submitStr, 4);
                if(!noThread)
                {
                    Thread thread = new Thread(this);
                    thread.start();
                }
            } catch (Exception ex) {
                status = ConnectStatus.CONNECT_SERVER_FAIL;
            }
        }
        if(!noThread){
            if (status == ConnectStatus.SUCCESS) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (m_connected) {
                            try {
                                boolean sleep = true;
                                synchronized (m_sendDatas) {
                                    if(m_sendDatas.size() > 0){
                                        for(int i = 0; i < m_sendDatas.size(); i++){
                                            m_socket.getOutputStream().write(m_sendDatas.get(i));
                                            m_socket.getOutputStream().flush();
                                        }
                                        m_sendDatas.clear();
                                    }
                                }
                                if (sleep) {
                                    Thread.sleep(1);
                                } 
                            } catch (Exception ex) {

                            }
                        }
                    }
                }).start();
            }
        }
        return status;
    }

    private ConnectStatus connectByHttp() {
        return ConnectStatus.SUCCESS;
    }

    private ConnectStatus connectBySock4() {
        return ConnectStatus.SUCCESS;
    }

    private ConnectStatus connectBySock5() {
        return ConnectStatus.SUCCESS;
    }

    private ConnectStatus connectProxyServer() {
        return ConnectStatus.SUCCESS;
    }

    public void delete() {
        if (!m_isDeleted) {
            close();
            m_connected = false;
            m_isDeleted = true;
        }
    }

    public void run() {
        byte[] str = null;
        boolean get = false;
        int head = 0, pos = 0, strRemain = 0, bufferRemain = 0, index = 0, len = 0;
        int intSize = 4;
        byte[] headStr = new byte[4];
        int headSize = 4;
        while (m_connected) {
            try {
                byte[] buffer = new byte[1024];
                len = m_inputStream.read(buffer);
                if (len == 0 || len == -1) {
                    byte[] rmsg = new byte[1];
                    rmsg[0] = (byte) ((char) 3);
                    FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg, 1);
                    FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 3, "socket error");
                    break;
                }
                index = 0;
                while (index < len) {
                    int diffSize = 0;
                    if (!get) {
                        diffSize = intSize - headSize;
                        if (diffSize == 0) {
                            head = (0xff & buffer[index]) | (0xff00 & (buffer[index + 1] << 8))
                                    | (0xff0000 & (buffer[index + 2] << 16)) | (0xff000000 & (buffer[index + 3] << 24));
                        } else {
                            for (int i = 0; i < diffSize; i++) {
                                headStr[headSize + i] = buffer[i];
                            }
                            head = (0xff & headStr[0]) | (0xff00 & (headStr[1] << 8))
                                    | (0xff0000 & (headStr[2] << 16)) | (0xff000000 & (headStr[3] << 24));
                        }
                        if (str != null) {
                            str = null;
                        }
                        str = new byte[head];
                        if (diffSize > 0) {
                            for (int i = 0; i < headSize; i++) {
                                str[i] = headStr[i];
                            }
                            pos += headSize;
                            headSize = intSize;
                        }
                    }
                    bufferRemain = len - index;
                    strRemain = head - pos;
                    get = strRemain > bufferRemain;
                    int remain = Math.min(strRemain, bufferRemain);
                    System.arraycopy(buffer, index, str, pos, remain);
                    pos += remain;
                    index += remain;
                    if (!get) {
                        FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, str, head);
                        head = 0;
                        pos = 0;
                        if (len - index == 0 || len - index >= intSize) {
                            headSize = intSize;
                        } else {
                            headSize = bufferRemain - strRemain;
                            for (int j = 0; j < headSize; j++) {
                                headStr[j] = buffer[index + j];
                            }
                            break;
                        }
                    }
                }
            } catch (Exception ex) {
                break;
            }
        }
        byte[] rmsg = new byte[1];
        rmsg[0] = (byte) ((char) 2);
        FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg, 1);
        FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 2, "socket exit");
        m_connected = false;
    }

    public int send(byte[] str, int len) {
        if (m_socket == null || !m_connected) {
            return -1;
        }
        int ret = -1;
        if(noThread){
            try{
                m_socket.getOutputStream().write(str);
                m_socket.getOutputStream().flush();
            }catch(Exception ex){   
            }
        }else{
            try {
                synchronized (m_sendDatas) {
                    m_sendDatas.add(str);
                }
                ret = len;
            } catch (Exception ex) {
                ret = -1;
            }
        }
        return ret;
    }

    public static enum ConnectStatus {

        SUCCESS,
        CONNECT_PROXY_FAIL,
        NOT_CONNECT_PROXY,
        CONNECT_SERVER_FAIL;

        public int getValue() {
            return this.ordinal();
        }

        public static ConnectStatus forValue(int value) {
            return ConnectStatus.values()[value];
        }
    }
}
